package com.hjr.calculator;


import java.math.BigDecimal;
import java.util.Deque;
import java.util.LinkedList;

public class Calculator implements ICalculator {


    private final Deque<CalcCmd> cmdHistory;
    private final Deque<BigDecimal> res;

    private CalcCmd currCmd;

    public Calculator() {
        this.cmdHistory = new LinkedList<>();
        this.res = new LinkedList<>();
        currCmd = new CalcCmd();
    }

    @Override
    public BigDecimal exec(String str) {
        if (NumberUtil.isNumber(str)) {
            if (currCmd.getNum1() == null) {
                currCmd.setNum1(new BigDecimal(str));
            } else if (currCmd.getOp() != null) {
                currCmd.setNum2(new BigDecimal(str));
            }
        } else {
            CalcOp op = CalcOp.getByOp(str);
            if (op == null)
                throw new UnsupportedOperationException(String.format("不支持计算操作符[%s]", op));
            if (!op.equals(CalcOp.EQUALS) && currCmd.getOp() == null && currCmd.getNum1() != null) {
                currCmd.setOp(op.getOp());
            }
            if (currCmd.getNum2() != null) {
                res.add(currCmd.exec());
                cmdHistory.add(currCmd);
                resetCmd(op);
            }
        }
        return res.isEmpty() ? BigDecimal.ZERO : res.getLast();
    }

    @Override
    public BigDecimal redo() {
        if (cmdHistory.isEmpty()) {
            return BigDecimal.ZERO;
        }
        CalcCmd lastCmd = cmdHistory.getLast();
        lastCmd.setNum1(res.getLast());
        BigDecimal ans = lastCmd.exec();
        res.add(ans);
        cmdHistory.add(lastCmd);
        resetCmd(null);
        return ans;
    }

    @Override
    public BigDecimal undo() {
        if (!cmdHistory.isEmpty()) {
            cmdHistory.removeLast();
            res.removeLast();
        }
        if (res.isEmpty()) {
            currCmd = new CalcCmd();
        }
        resetCmd(null);
        return res.isEmpty() ? BigDecimal.ZERO : res.getLast();
    }

    public BigDecimal getResult() {
        return res.isEmpty() ? BigDecimal.ZERO : res.getLast();
    }

    private void resetCmd(CalcOp op) {
        currCmd = new CalcCmd();
        currCmd.setNum1(!res.isEmpty() ? res.getLast() : null);
        currCmd.setOp((op == null || CalcOp.EQUALS == op) ? null : op.getOp());
    }


}
